#include "NDK.h"
#include "Debug.h"
#include "Trace.h"
#include "InetAddr.h"
#include "TimeValue.h"
#include "ReactorImpl.h"

#if defined NDK_LINUX
#include <errno.h>
#include <ctype.h>
#include <malloc.h>
#include <string.h>
#include <sys/select.h>
#include <sys/resource.h>
#elif defined NDK_WIN32

#endif

TimeValue NDK::gettimeofday ()
{
#if defined (NDK_WIN32)
    FILETIME   tfile;
    ::GetSystemTimeAsFileTime (&tfile);
    return TimeValue (tfile);
#elif defined NDK_LINUX
    struct timeval tv;
    ::gettimeofday (&tv, 0);
    return TimeValue (tv);
#endif
}
int NDK::max_handles ()
{
    TRACE ("NDK");
#if defined NDK_LINUX
    struct rlimit rl;
    ::memset ((void *)&rl, 0, sizeof (rl));
    int r = ::getrlimit (RLIMIT_NOFILE, &rl);
#   if !defined (RLIM_INFINITY)
    if (r == 0)
        return rl.rlim_cur;
#   else
    if (r == 0 && rl.rlim_cur != RLIM_INFINITY)
        return rl.rlim_cur;
#   endif
#endif
    return -1;
}
int NDK::set_handle_limit (int new_limit, 
        int increase_limit_only/* = 0*/)
{
#if defined (NDK_LINUX)
    TRACE ("NDK");
    int cur_limit = NDK::max_handles ();
    int max_limit = cur_limit;
    if (cur_limit == -1)
        return -1;
    struct rlimit rl;
    ::memset ((void *)&rl, 0, sizeof (rl));
    int r = ::getrlimit (RLIMIT_NOFILE, &rl);
    if (r == 0)
        max_limit = rl.rlim_max;
    if (new_limit < 0)
        return -1;

    if (new_limit > cur_limit)
    {
        rl.rlim_cur = new_limit;
        return ::setrlimit (RLIMIT_NOFILE, &rl);
    }
    else if (increase_limit_only == 0)
    {
        rl.rlim_cur = new_limit;
        return ::setrlimit (RLIMIT_NOFILE, &rl);
    }
#endif
    return 0;
}
//************************************I/O Operation*****************************
int NDK::handle_ready (NDK_HANDLE handle, 
        int r_ready, 
        int w_ready, 
        int e_ready, 
        const TimeValue* timeout) 
{
    int width = handle + 1; //
    fd_set rset, wset, eset;

    // must define here and not define in while loop
    struct timeval tv;
    if (timeout != 0)
    {
        tv = *timeout;
    }
    int ret_val = 0;
    while (1)
    {
        FD_ZERO (&rset);
        FD_SET (handle, &rset);
        ret_val = ::select (width, r_ready ? &rset : 0, 
                w_ready ? &(wset = rset) : 0, 
                e_ready ? &(eset = rset) : 0,
                timeout == 0 ? 0 : &tv);
        // not clear the fd_set return -1, POSIX 1.0v: timeval would be modify
        // clear all the fd_set return 0 (timeout)
        if ((ret_val == -1) && (errno == EINTR))// interupt by signal
            continue;
        break;  // timeout or ready
    }
    return ret_val;
}
int NDK::recv (NDK_HANDLE handle, 
        void *buff, 
        size_t len, 
        int flags, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    // complete block
    if (timeout == 0)  // block mode, handle by tcp stack
    {
        do{
            ret = NDK_OS::recv (handle, buff, len, flags);
        }while (ret == -1 && errno == EINTR);
        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);

    // try it first
    ret = NDK_OS::recv (handle, buff, len, flags);
    if (ret >= 0 // recv successfuly
            ||(ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN)) 
        // socket error
        return ret;
    // timeout
    if (*timeout > TimeValue::zero)
    {
        ret = NDK::handle_read_ready (handle, timeout);
        if (ret	> 0) 
        {
            ret = NDK_OS::recv (handle, buff, len, flags);
        }else  // timeout or error
            ret = -1;
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret; // return -1 if no data in tcp stack buff under non-block mode
}

int NDK::recv_n (NDK_HANDLE handle, 
        void *buff, 
        size_t len, 
        int flags, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t recv_bytes = 0;
    int error = 0;
    int result = 0;
    // set nonblock
    int flag = 0;
    if (timeout != 0)
        NDK::record_and_set_non_block_mode (handle, flag);
    //
    for (; recv_bytes < len; recv_bytes += n)
    {
        n = NDK_OS::recv (handle, 
                static_cast<char *>(buff) + recv_bytes, 
                len - recv_bytes, 
                flags);
        if (n == 0 ||  // peer closed
                n == -1)  // check for errors
        {
            // check for possible blocking.
            if (n == -1 && 
                    (errno == EWOULDBLOCK || errno == EAGAIN))
            {
                if (timeout == 0 || (timeout != 0 && *timeout > TimeValue::zero))
                {
                    if (NDK::handle_read_ready (handle, timeout) > 0) 
                    {
                        n = 0;  // !!
                        continue;
                    }
                }
            }else if (n == -1 && errno == EINTR)  // normal error
            {
                n = 0;  // !!
                continue;  
            }else
                error = 1;
            // if select() timeout, should return the bytes recv actually
            result = n;
            break;
        }
    }
    if (error)
        return result;
    // reset to block mode
    if (timeout != 0)
        NDK::restore_non_block_mode (handle, flag);
    return recv_bytes;
}
int NDK::recvv (NDK_HANDLE handle, 
        iovec iov[], 
        size_t count, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    if (timeout == 0)
    {
        do{
            ret = NDK_OS::readv (handle, iov, count);
        }while (ret == -1 && errno == EINTR);

        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);
    // try it first
    ret = NDK_OS::readv (handle, iov, count);
    if (ret >= 0 // recv successfuly
            ||(ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN)) 
        // socket error
        return ret;
    // timeout
    if (*timeout > TimeValue::zero)
    {
        ret = NDK::handle_read_ready (handle, timeout);
        if (ret	> 0) 
        {
            ret = NDK_OS::readv (handle, iov, count);
        }else
            ret = -1;
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret;
}
int NDK::recvv_n (NDK_HANDLE handle, 
        iovec iv[], 
        size_t count, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t recv_bytes = 0;
    int error = 0;
    int result = 0;
#if IOVEC_SECURITY_IO
    // for bak
    struct iovec *iov = iv;
    struct iovec *iov_bak = 
        (struct iovec*)::malloc (sizeof(struct iovec) * count);
    for (size_t i = 0; i < count; ++i)
        iov_bak[i] = iov[i]; 
    bool bfull = true;
#else
    struct iovec *iov = iv;
#endif
    // set nonblock
    int flag = 0;
    if (timeout != 0)
        NDK::record_and_set_non_block_mode (handle, flag);
    for (size_t s = 0; s < count;)
    {
        n = NDK_OS::readv (handle, iov + s, count - s);
        if (n == 0 || // peer closed
                n == -1)  // check for errors
        {
            // check for possible blocking.
            if (n == -1 && 
                    (errno == EAGAIN || errno == EWOULDBLOCK))
            {
                if (timeout == 0 || (timeout != 0 && *timeout > TimeValue::zero))
                {
                    if (NDK::handle_read_ready (handle, timeout) > 0) 
                    {
                        n = 0;  // !!
                        continue;
                    }
                }
            }else if (n == -1 && errno == EINTR) 
            {
                n = 0;  // !!
                continue;  
            }else
                error = 1;
            result = n;
            break;
        }
        for (recv_bytes += n; 
                s < count && n >= static_cast<int>(iov[s].iov_len); 
                ++s)
            n -= iov[s].iov_len;
#if IOVEC_SECURITY_IO
        if (n != 0)
        {
            bfull = false;
            char *base = static_cast<char *> (iov[s].iov_base);
            iov[s].iov_base = base + n;
            iov[s].iov_len  = iov[s].iov_len - n;
        }
#endif
    }
#if IOVEC_SECURITY_IO
    if (!bfull)
    {
        for (size_t i = 0; i < count; ++i)
            iov[i] = iov_bak[i]; 
    }
    ::free (iov_bak);
#endif
    // reset to block mode
    if (timeout != 0)
        NDK::restore_non_block_mode (handle, flag);
    if (error)
        return result;
    return recv_bytes;
}
int NDK::recvfrom (NDK_HANDLE handle, 
        void *buff, 
        size_t len, 
        int flags, 
        struct sockaddr *from, 
        int *fromlen,
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    // complete block
    if (timeout == 0)  // block mode, handle by tcp stack
    {
        do{
            ret = NDK_OS::recvfrom (handle, buff, len, flags, from, fromlen);
        }while (ret == -1 && errno == EINTR);
        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);

    // try it first
    ret = NDK_OS::recvfrom (handle, buff, len, flags, from, fromlen);
    if (ret >= 0 // recv successfuly
            ||(ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN)) 
        // socket error
        return ret;
    // timeout
    if (*timeout > TimeValue::zero)
    {
        ret = NDK::handle_read_ready (handle, timeout);
        if (ret	> 0) 
        {
            ret = NDK_OS::recvfrom (handle, buff, len, flags, from, fromlen);
        }else  // timeout or error
            ret = -1;
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret; // return -1 if no data in tcp stack buff under non-block mode
}
int NDK::send (NDK_HANDLE handle, 
        const void *buff, 
        size_t len, 
        int flags, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    if (timeout == 0)
    {
        do{
            ret = NDK_OS::send (handle, buff, len, flags);
        }while (ret == -1 && errno == EINTR);
        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);

    // try it first
    ret = NDK_OS::send (handle, buff, len, flags);
    if (ret == -1 
            && (errno == EAGAIN 
                || errno == EWOULDBLOCK 
                || errno == ENOBUFS))
    {
        // timeout
        if (*timeout > TimeValue::zero)
        {
            if (NDK::handle_read_ready (handle, timeout) > 0) 
                // <= 0 timeout not do send 
                // > 0 try it again
                ret = NDK_OS::send (handle, buff, len, flags);
        }
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret;
}
int NDK::send_n (NDK_HANDLE handle, 
        const void *buff, 
        size_t len, 
        int flags, 
        const TimeValue* timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t send_bytes = 0;
    int error = 0;
    int result = 0;
    // set nonblock mode
    int flag = 0;
    if (timeout != 0)
        NDK::record_and_set_non_block_mode (handle, flag);
    for (; send_bytes < len; send_bytes += n)
    {
        n = NDK_OS::send (handle, 
                static_cast<const char *>(buff) + send_bytes, 
                len - send_bytes, 
                flags);
        if (n == 0 || // peer closed
                n == -1)  // check for errors
        {
            // check for possible blocking.
            if (n == -1 &&
                    (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS))
            {
                // note msec not be modifyed. so not exact
                int ret = NDK::handle_write_ready (handle, timeout);  
                if (ret > 0) // time out or not ready handle
                {
                    n = 0;  // !!
                    continue;
                }
            }else if (n == -1 && errno == EINTR) 
            {
                n = 0;  // !!
                continue;  
            }else
                error = 1;

            result = n;
            break;
        }
    }
    // reset to block mode
    if (timeout != 0)
        NDK::restore_non_block_mode (handle, flag);
    if (error)
        return result;
    return send_bytes;
}
int NDK::sendv (NDK_HANDLE handle, 
        const iovec iov[], 
        size_t count, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    if (timeout == 0)
    {
        do{
            ret = NDK_OS::writev (handle, iov, count);
        }while (ret == -1 && errno == EINTR);
        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);

    // try it first
    ret = NDK_OS::writev (handle, iov, count);
    if (ret == -1 && 
            (errno == EAGAIN 
             || errno == EWOULDBLOCK 
             || errno == ENOBUFS))
    {
        // timeout
        if (*timeout > TimeValue::zero)
        {
            if (NDK::handle_write_ready (handle, timeout) > 0) 
                // <= 0 timeout not do writev 
                // try it again
                ret = NDK_OS::writev (handle, iov, count);
        }
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret;
}
int NDK::sendv_n (NDK_HANDLE handle, 
        const iovec iov_[], 
        size_t count, 
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t send_bytes = 0;
    int result = 0;
    int error  = 0;
#if IOVEC_SECURITY_IO
    // for bak
    struct iovec *iov = const_cast<iovec*>(iov_);
    struct iovec *iov_bak = (struct iovec*)::malloc (sizeof(struct iovec) * count);
    for (size_t i = 0; i < count; ++i)
        iov_bak[i] = iov[i]; 
    bool bfull = true;
#else
    struct iovec *iov = const_cast<iovec*>(iov_);
#endif
    int flag = 0;
    if (timeout != 0)
        NDK::record_and_set_non_block_mode (handle, flag);
    for (size_t s = 0; s < count;)
    {
        n = NDK_OS::writev (handle, iov + s, count - s);
        if (n == 0 ||  // peer closed
                n == -1)  // check for errors
        {
            // check for possible blocking.
            if (n == -1 &&
                    (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS))
            {
                // note msec not be modifyed. so not exact
                int ret = NDK::handle_write_ready (handle, timeout);  
                if (ret > 0) 
                {
                    n = 0;  // !!
                    continue;
                }
            }else if (n == -1 && errno == EINTR) 
            {
                n = 0;  // !!
                continue;  
            }else 
                error = 1;

            result = n;
            break;
        }
        for (send_bytes += n; 
                s < count && n >= static_cast<int>(iov[s].iov_len); 
                ++s)
            n -= iov[s].iov_len;
#if IOVEC_SECURITY_IO
        if (n != 0)
        {
            bfull = false;
            char *base = static_cast<char *> (iov[s].iov_base);
            iov[s].iov_base = base + n;
            iov[s].iov_len  = iov[s].iov_len - n;
        }
#endif
    }
#if IOVEC_SECURITY_IO
    if (!bfull)
    {
        for (size_t i = 0; i < count; ++i)
            iov[i] = iov_bak[i]; 
    }
    ::free (iov_bak);
#endif
    // reset to block mode
    if (timeout)
        NDK::restore_non_block_mode (handle, flag);
    return send_bytes;
}
int NDK::sendto (NDK_HANDLE handle, 
        const void *buff, 
        size_t len, 
        int flags, 
        const struct sockaddr *to,
        int tolen,
        const TimeValue *timeout) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    if (timeout == 0)
    {
        do{
            ret = NDK_OS::sendto (handle, buff, len, flags, to, tolen);
        }while (ret == -1 && errno == EINTR);
        return ret;
    }

    // set nonblock mode
    int flag = 0;
    NDK::record_and_set_non_block_mode (handle, flag);

    // try it first
    ret = NDK_OS::sendto (handle, buff, len, flags, to, tolen);
    if (ret == -1 
            && (errno == EAGAIN 
                || errno == EWOULDBLOCK 
                || errno == ENOBUFS))
    {
        // timeout
        if (*timeout > TimeValue::zero)
        {
            if (NDK::handle_read_ready (handle, timeout) > 0) 
                // <= 0 timeout not do send 
                // > 0 try it again
                ret = NDK_OS::sendto (handle, buff, len, flags, to, tolen);
        }
    }
    // reset to block mode
    NDK::restore_non_block_mode (handle, flag);
    return ret;
}
int NDK::read (NDK_HANDLE handle, 
        void *buff, 
        size_t len) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    do{
        ret = NDK_OS::read (handle, buff, len);
    }while (ret == -1 && ret == EINTR);
    return ret;
} 
int NDK::read_n (NDK_HANDLE handle, 
        void *buff, 
        size_t len) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t read_bytes = 0;
    for (; read_bytes < len; read_bytes += n)
    {
        n = NDK_OS::read (handle, 
                static_cast<char *>(buff) + read_bytes, 
                len - read_bytes);
        if (n == -1 || n == 0)  // check for errors
        {
            return n;
        }
    }
    return read_bytes;
}
int NDK::write (NDK_HANDLE handle, 
        const void *buff, 
        size_t len) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    int ret = 0;
    do{
        ret = NDK_OS::write (handle, buff, len);
    }while (ret == -1 && ret == EINTR);

    return ret;
} 
int NDK::write_n (NDK_HANDLE handle, 
        const void *buff, 
        size_t len) 
{
    if (handle == NDK_INVALID_HANDLE)
        return -1;
    ssize_t n = 0;
    size_t write_bytes = 0;
    for (; write_bytes < len; write_bytes += n)
    {
        n = NDK_OS::write (handle, 
                static_cast<const char *>(buff) + write_bytes, 
                len - write_bytes);
        if (n == 0 || n == -1) 
            return n;
    }
    return write_bytes;
}
int NDK::bind_port (NDK_HANDLE handle, 
        size_t ip_addr, 
        int addr_family)
{
    InetAddr addr;
    addr.set (0, ip_addr);
    unused_arg (addr_family);
#if 0
    return ::bind (handle, (sockaddr *)addr.get_addr (),
            addr.get_addr_size ());
#endif
    unsigned short upper_limit = MAX_DEFAULT_PORT;
    // We have to select the port explicitly.
    for (;;)
    {
        addr.set (upper_limit, ip_addr);
        if (NDK_OS::bind (handle, (sockaddr *)addr.get_addr (),
                    addr.get_addr_size ()) == 0)
            return 0;
        else if (errno != EADDRINUSE)
            return -1;
        else
        {
            --upper_limit;
        }
    }
}
int NDK::get_cpu_count (void)
{
#if defined NDK_WIN32
    SYSTEM_INFO sysinfo;
    ::GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#elif defined NDK_LINUX
    int ret = sysconf(_SC_NPROCESSORS_CONF) ;
    if (ret == -1) ret = 1;
    return ret;
#endif
}
// Quote from <Apache Portable Run-time libraries>
char *NDK::strtok (char *str, const char *sep, char **last)
{
    char *token;

    if (!str)           /* subsequent call */
        str = *last;    /* start where we left off */

    /* skip characters in sep (will terminate at '\0') */
    while (*str && strchr(sep, *str))
        ++str;

    if (!*str)          /* no more tokens */
        return NULL;

    token = str;

    /* skip valid token characters to terminate token and
     *      * prepare for the next call (will terminate at '\0) 
     *           */
    *last = token + 1;
    while (**last && !strchr(sep, **last))
        ++*last;

    if (**last) {
        **last = '\0';
        ++*last;
    }

    return token;
}
char *NDK::strstrip (const char *in_str, char *out_str, int len)
{
    const char *last  = NULL;
    const char *begin = NULL;

    if (in_str == NULL || out_str == NULL || len <= 0) 
        return NULL;

    while (isspace ((int)*in_str) && *in_str) 
        in_str++;
    begin = in_str;

    for (; *in_str; ++in_str)
    {
        if (isspace((int)*in_str) == 0)
            last = in_str;
    }
    int l = last - begin + 1;
    if (l < len)
        len = l;
    strncpy (out_str, begin, len);
    return out_str ;
}
char *NDK::strstrip_all (const char *in_str, char *out_str, int len)
{
    if (in_str == NULL || out_str == NULL) 
        return NULL;

    char *s = out_str;
    for (int i = 0; *in_str && i < len; ++in_str)
    {
        if (isspace((int)*in_str) == 0)
            *s++ = *in_str;
    }
    return s;
}
THREAD_FUNC_RETURN_T NDK::thr_run_reactor_event_loop (void *arg)
{
    ReactorImpl *r = reinterpret_cast<ReactorImpl *>(arg);
    if (r == 0)
        return 0;
    r->owner (Thread::self ());

    while (1)
    {
        int result = r->handle_events ();
        if (result == -1 && r->deactivated ())
            return 0;
        else if (result == -1)
            return 0;
    }
    return 0;
}


